/*
 * This is written by Lulu Han.
 * E-mail: locomotive_crypto@163.com
 */

#ifndef PPSPD_PAILLIER_H
#define PPSPD_PAILLIER_H

#include <NTL/ZZ.h>

/*Implement Paillier cryptosystem*/

class Paillier{
public:
	// Public key class
	class PublicKey{
	public:
		PublicKey();
		PublicKey(const NTL::ZZ& N);
		
		// Get the parameter for Paillier
		const NTL::ZZ GetN();
		const NTL::ZZ GetG();
		const NTL::ZZ GetNSquare();
	protected: // used for subclass
		NTL::ZZ m_g;
		NTL::ZZ m_N;
		NTL::ZZ m_N_square;
	};
	
	// Private key class
	class PrivateKey: public PublicKey{
	public:
		PrivateKey();
		PrivateKey(const NTL::ZZ& p, const NTL::ZZ& q);
		
		// Get the parameter for Paillier
		const NTL::ZZ GetPhiN();
		const NTL::ZZ GetPhiNInv();
	private:
		NTL::ZZ m_Phi_N;
		NTL::ZZ m_Phi_N_inv;
	};
	
	class Encryptor: public PublicKey{
	public:
		Encryptor(const PublicKey& pub);
		
		// Generating ciphertext c by encrypting plaintext m
		void Encrypt(const NTL::ZZ& m, NTL::ZZ& c);
		
		// Homomorphic operators over encrypted values
		void HE_Add_Cipher(const NTL::ZZ& c1, const NTL::ZZ& c2, NTL::ZZ& c);
		void HE_Add_Const(const NTL::ZZ& c1, const NTL::ZZ& n, NTL::ZZ& c);
		void HE_Mul_Const(const NTL::ZZ& c1, const NTL::ZZ& n, NTL::ZZ& c);
	};
	
	class Decryptor:public PrivateKey{
	public:
		Decryptor(const PrivateKey& pri);
		
		// Generate plaintext m by decrypting ciphertext c 
		void Decrypt(const NTL::ZZ& c, NTL::ZZ& m);
		
		// AN additional memeber function
		void Encrypt(const NTL::ZZ& m, NTL::ZZ& c);
	};
	
public:
	// Static memeber functions
	// Generate a random key pair with p and q equal to bitlens
	static void GenKeyPair(PrivateKey& pri, PublicKey& pub, long bitlens);
	
	// Generate a key pair based on p and q inputed by user
	static void GenKeyPair(PrivateKey& pri, PublicKey& pub, const NTL::ZZ& p, const NTL::ZZ& q);
};

// Define new names for some classs
using PrivateKey = Paillier::PrivateKey;
using PublicKey = Paillier::PublicKey;
using Decryptor = Paillier::Decryptor;
using Encryptor = Paillier::Encryptor;

#endif // PPSPD_PAILLIER_H
